using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.Collections;
using System.Drawing;
using System.Runtime.Serialization;

namespace Skywave.Windows.Forms
{
    public delegate void SheetCellOwnerDrawEventHandler(Graphics g, Rectangle rect, SheetCell cell, int rowIndex, int colIndex);

    [Serializable()]
    public class Sheet : ISerializable, IPrintOn
    {
        public event SheetCellOwnerDrawEventHandler SheetCellOwnerDraw;
        private string BuildKey(int i, int j)
        {
            return i + ", " + j;
        }
        public SheetDefaults Defaults = new SheetDefaults();
        private Hashtable mDRows = new Hashtable(), mDColumns = new Hashtable(), mDCells = new Hashtable();
        public DigitTypes DigitType = DigitTypes.Persian;
        private SheetPrintSettings autoPageSettings = new SheetPrintSettings();
        public Sheet()
        {
            Defaults.Font = new Font("Tahoma", 10);
            Defaults.RowHeight = 20;
            Defaults.ColumnWidth = 60;
            Defaults.ForeColor = Color.Black;
            Defaults.BackColor = Color.White;
            Defaults.BorderColor = Color.Black;
            Defaults.SetBorder(SheetBorderTypes.Left, -1);
            Defaults.SetBorder(SheetBorderTypes.Right, -1);
            Defaults.SetBorder(SheetBorderTypes.Top, -1);
            Defaults.SetBorder(SheetBorderTypes.Bottom, -1);
            Defaults.TextAlign = ContentAlignment.MiddleLeft;
            Defaults.RightToLeft = false;
            Defaults.ShrinkToFit = false;
            Defaults.Warp = false;
            Defaults.VerticalText = false;

            autoPageSettings.ScaleToFitHorizontally = false;
            autoPageSettings.ScaleToFitVertically = false;
            autoPageSettings.KeepAspectRatio = true;
        }

        private RightToLeft mRightToLeft = RightToLeft.No;
        public RightToLeft RightToLeft
        {
            get { return mRightToLeft; }
            set { mRightToLeft = value; }
        }

        //int mPrintAreaRow, mPrintAreaColumn;
        public int PrintAreaRow
        {
            get { return mPrintAreaEndCell.Y; }
            set { mPrintAreaEndCell.Y = value; }
        }

        public int PrintAreaColumn
        {
            get { return mPrintAreaEndCell.X; }
            set { mPrintAreaEndCell.X = value; }
        }

        private Point mPrintAreaStartCell = new Point(1, 1);
        public Point PrintAreaStartCell
        {
            get { return mPrintAreaStartCell; }
            set { mPrintAreaStartCell = value; }
        }

        private Point mPrintAreaEndCell = new Point(1, 1);
        public Point PrintAreaEndCell
        {
            get { return mPrintAreaEndCell; }
            set { mPrintAreaEndCell = value; }
        }

        private int mHeaderStartRow;
        public int HeaderStartRow
        {
            get { return mHeaderStartRow; }
            set { mHeaderStartRow = value; }
        }

        private int mHeaderEndRow;
        public int HeaderEndRow
        {
            get { return mHeaderEndRow; }
            set { mHeaderEndRow = value; }
        }

        public RowsIterator Rows
        {
            get
            {
                return new RowsIterator(this);
            }
        }

        public class RowsIterator
        {
            readonly Sheet outer;

            internal RowsIterator(Sheet outer)
            {
                this.outer = outer;
            }

            public SheetRow this[int index]
            {
                get
                {
                    if (outer.mDRows.ContainsKey(index))
                    {
                        return (SheetRow)outer.mDRows[index];
                    }
                    else
                    {
                        outer.mDRows.Add(index, new SheetRow());
                        return (SheetRow)outer.mDRows[index];
                    }
                }
                set
                {
                    if (outer.mDRows.ContainsKey(index))
                    {
                        outer.mDRows[index] = value;
                    }
                    else
                    {
                        outer.mDRows.Add(index, value);
                    }
                }
            }

            public System.Collections.Generic.IEnumerator<SheetRow> GetEnumerator()
            {
                foreach (SheetRow xRow in outer.mDRows)
                {
                    yield return xRow;
                }
            }
        }

        public ColumnsIterator Columns
        {
            get
            {
                return new ColumnsIterator(this);
            }
        }

        public class ColumnsIterator
        {
            readonly Sheet outer;

            internal ColumnsIterator(Sheet outer)
            {
                this.outer = outer;
            }

            public SheetColumn this[int index]
            {
                get
                {
                    if (outer.mDColumns.ContainsKey(index))
                    {
                        return (SheetColumn)outer.mDColumns[index];
                    }
                    else
                    {
                        outer.mDColumns.Add(index, new SheetColumn());
                        return (SheetColumn)outer.mDColumns[index];
                    }
                }
                set
                {
                    if (outer.mDColumns.ContainsKey(index))
                    {
                        outer.mDColumns[index] = value;
                    }
                    else
                    {
                        outer.mDColumns.Add(index, value);
                    }
                }
            }

            public System.Collections.Generic.IEnumerator<SheetColumn> GetEnumerator()
            {
                foreach (SheetColumn xCol in outer.mDColumns)
                {
                    yield return xCol;
                }
            }
        }

        public CellsIterator Cells
        {
            get
            {
                return new CellsIterator(this);
            }
        }

        public class CellsIterator
        {
            readonly Sheet outer;

            internal CellsIterator(Sheet outer)
            {
                this.outer = outer;
            }

            public int Length { get { return 1; } }

            public SheetCell this[int rowIndex, int columnIndex]
            {
                get
                {
                    string key = outer.BuildKey(rowIndex, columnIndex);
                    if (outer.mDCells.ContainsKey(key))
                    {
                        return (SheetCell)outer.mDCells[key];
                    }
                    else
                    {
                        outer.mDCells.Add(key, new SheetCell());
                        return (SheetCell)outer.mDCells[key];
                    }
                }
                set
                {
                    string key = outer.BuildKey(rowIndex, columnIndex);
                    if (outer.mDCells.ContainsKey(key))
                    {
                        outer.mDCells[key] = value;
                    }
                    else
                    {
                        outer.mDCells.Add(key, value);
                    }
                }
            }

            public System.Collections.Generic.IEnumerator<SheetCell> GetEnumerator()
            {
                foreach (SheetCell xCell in outer.mDCells)
                {
                    yield return xCell;
                }
            }
        }
        private object mTag;

        public object Tag
        {
            get { return mTag; }
            set { mTag = value; }
        }

        public SheetRange Range(int row1, int column1, int row2, int column2)
        {
            return (new SheetRange(row1, column1, row2, column2, mDCells));
        }

        private Dictionary<int, SheetRange> pages;
        private Rectangle pagingRect;
        private void MakePages(Rectangle rect, SheetPagingDirections pagingDirection)
        {
            int sRow, sCol, eRow, eCol;
            int pH, pW;
            pages = new Dictionary<int, SheetRange>();
            pagingRect = rect;
            int page = 1;
            switch (pagingDirection)
            {
                case SheetPagingDirections.HorizontalPriority:
                    if (mHeaderStartRow >= mPrintAreaStartCell.Y && mHeaderEndRow >= mPrintAreaStartCell.Y)
                    {
                        sRow = eRow = mHeaderEndRow + 1;
                    }
                    else
                    {
                        sRow = eRow = mPrintAreaStartCell.Y;
                    }
                    while (eRow <= mPrintAreaEndCell.Y)
                    {
                        sCol = eCol = mPrintAreaStartCell.X;
                        pH = GetRowsHeight(mHeaderStartRow, mHeaderEndRow);
                        while ((pH + GetRowsHeight(sRow, eRow + 1) <= rect.Height) && (eRow <= mPrintAreaEndCell.Y)) eRow++;
                        if (eRow > mPrintAreaEndCell.Y) eRow = mPrintAreaEndCell.Y;
                        eCol = mPrintAreaStartCell.X;
                        while (eCol < mPrintAreaEndCell.X)
                        {
                            pW = 0;
                            while ((pW + GetColumnsWidth(sCol, eCol + 1) <= rect.Width) && (eCol <= mPrintAreaEndCell.X)) eCol++;
                            if (eCol > mPrintAreaEndCell.X) eCol = mPrintAreaEndCell.X;
                            pages.Add(page, Range(sRow, sCol, eRow, eCol));
                            page++;
                            eCol++;
                            sCol = eCol;
                        }
                        eRow++;
                        sRow = eRow;
                    }
                    break;
                case SheetPagingDirections.VerticalPriority:
                    break;
                default:
                    break;
            }
        }

        private int GetRowsHeight(int startRowIndex, int endRowIndex)
        {
            int resault = 0;
            if (endRowIndex == 0) return 0;
            for (int r = startRowIndex; r <= endRowIndex; r++)
            {
                if (mDRows.Contains(r) && ((SheetRow)mDRows[r]).Height >= 0)
                {
                    resault += ((SheetRow)mDRows[r]).Height;
                }
                else
                {
                    resault += Defaults.RowHeight;
                }
            }
            return resault;
        }

        private int GetColumnsWidth(int startIndex, int endIndex)
        {
            int resault = 0;
            if (endIndex == 0) return 0;
            for (int i = startIndex; i <= endIndex; i++)
            {
                if (mDColumns.Contains(i) && ((SheetColumn)mDColumns[i]).Width >= 0)
                {
                    resault += ((SheetColumn)mDColumns[i]).Width;
                }
                else
                {
                    resault += Defaults.ColumnWidth;
                }
            }
            return resault;
        }

        public Size Size
        {
            get
            {
                return new Size(GetColumnsWidth(mPrintAreaStartCell.X, mPrintAreaEndCell.X), GetRowsHeight(mPrintAreaStartCell.Y, mPrintAreaEndCell.Y));
            }
        }

        //public bool PrintPagedOn(Graphics g, Rectangle rect, SheetPagingDirections pagingDirection, int printPageNumber)
        //{
        //    mPrintSettings.AutoPage = true;
        //    mPrintSettings.PagingDirection = pagingDirection;
        //    mPrintSettings.CurrentPage = printPageNumber;
        //    mPrintSettings.Graphics = g;
        //    mPrintSettings.Bounds = rect;
        //    Print();
        //    return mPrintSettings.HasMorePages;
        //}

        public void PrintOn(Graphics g, Rectangle rect, bool ScaleToFitHorizontally, bool ScaleToFitVertically, bool KeepAspectRatio)
        {
            SheetPrintSettings mySPS = mPrintSettings.Clone() as SheetPrintSettings;
            mySPS.Graphics = g;
            mySPS.Bounds = rect;
            mySPS.ScaleToFitHorizontally = ScaleToFitHorizontally;
            mySPS.ScaleToFitVertically = ScaleToFitVertically;
            mySPS.KeepAspectRatio = KeepAspectRatio;
            mySPS.AutoPage = false;
            InternalPrint(mySPS, mPrintAreaStartCell, mPrintAreaEndCell);
            //Print();
        }

        public void Print()
        {
            SheetPagingDirections pagingDirection = mPrintSettings.PagingDirection;
            int printPageNumber = mPrintSettings.CurrentPage;

            if (mPrintSettings.AutoPage)
            {
                if (mPrintSettings.CurrentPage == 1) MakePages(mPrintSettings.Bounds, pagingDirection);
                //Point oldPASC = new Point(mPrintAreaStartCell.X, mPrintAreaStartCell.Y);
                //Point oldPAEC = new Point(mPrintAreaEndCell.X, mPrintAreaEndCell.Y);
                if (pages.Count > 0)
                {
                    InternalCopyPrintSettingsToAutopage();
                    Rectangle rect1 = autoPageSettings.Bounds;
                    Point tempStartCell = new Point();
                    Point tempEndCell = new Point();
                    tempStartCell.X = pages[printPageNumber].Column1;
                    tempEndCell.X = pages[printPageNumber].Column2;
                    if (mHeaderEndRow > 0)
                    {
                        tempStartCell.Y = mHeaderStartRow;
                        tempEndCell.Y = mHeaderEndRow;
                        InternalPrint(autoPageSettings, tempStartCell, tempEndCell);
                        //PrintOn(g, rect, false, false, true);
                        rect1.Y += GetRowsHeight(mHeaderStartRow, mHeaderEndRow);
                        autoPageSettings.Bounds = rect1;
                    }
                    tempStartCell.Y = pages[printPageNumber].Row1;
                    tempEndCell.Y = pages[printPageNumber].Row2;
                    InternalPrint(autoPageSettings, tempStartCell, tempEndCell);
                    //PrintOn(g, rect, false, false, true);
                    //mPrintAreaStartCell = oldPASC;
                    //mPrintAreaEndCell = oldPAEC;
                    mPrintSettings.HasMorePages = (printPageNumber < pages.Count);
                }
                if (mPrintSettings.HasMorePages)
                    mPrintSettings.CurrentPage++;
                else
                    mPrintSettings.CurrentPage = 1;
            }
            else
            {
                InternalPrint(mPrintSettings, mPrintAreaStartCell, mPrintAreaEndCell);
            }
        }

        private void InternalCopyPrintSettingsToAutopage()
        {
            autoPageSettings.Bounds = mPrintSettings.Bounds;
            autoPageSettings.CenterInBoundsHorizontally = mPrintSettings.CenterInBoundsHorizontally;
            autoPageSettings.CenterInBoundsVertically = mPrintSettings.CenterInBoundsVertically;
            autoPageSettings.Graphics = mPrintSettings.Graphics;
            autoPageSettings.PagingDirection = mPrintSettings.PagingDirection;
        }

        private void InternalPrint(SheetPrintSettings sps, Point startCell, Point endCell)
        {
            Graphics g = sps.Graphics;
            Rectangle bounds = sps.Bounds;
            string str1;

            if (g == null) return;
            if (!((sps.ScaleToFitHorizontally || sps.ScaleToFitVertically) && !sps.KeepAspectRatio))
            {
                int newX = sps.Bounds.X, newY = sps.Bounds.Y;
                if (mPrintSettings.CenterInBoundsHorizontally && bounds.Width > Size.Width)
                {
                    newX = bounds.Left + (int)((bounds.Width - Size.Width) / 2);
                }
                if (mPrintSettings.CenterInBoundsVertically && bounds.Height > Size.Height)
                {
                    newY = bounds.Top + (int)((bounds.Height - Size.Height) / 2);
                }
                bounds = new Rectangle(newX, newY, bounds.Width, bounds.Height);
            }
            int c, r;
            double scaleHeight = 1, scaleWidth = 1;
            SheetCell xCell;
            SheetColumn xColumn;
            SheetRow xRow;
            SheetBorderTypes sbt1;
            int sumWidth = 0, sumHeight = 0;
            SheetCell printCell;
            string key;
            int passedWidth = 0, passedHeight = 0;
            int cellWidth;
            int addValue;
            float textW, textH;
            int cS, cE, rS, rE;
            if (sps.ScaleToFitHorizontally)
            {
                for (c = startCell.X; c <= endCell.X; c++)
                {
                    if ((mDColumns.ContainsKey(c)) && (((SheetColumn)mDColumns[c]).Width >= 0))
                    {
                        sumWidth += ((SheetColumn)mDColumns[c]).Width;
                    }
                    else
                    {
                        sumWidth += Defaults.ColumnWidth;
                    }
                }
                scaleWidth = (double)bounds.Width / (double)sumWidth;
            }
            if (sps.ScaleToFitVertically)
            {
                for (r = startCell.Y; r <= endCell.Y; r++)
                {
                    if ((mDRows.ContainsKey(r)) && (((SheetRow)mDRows[r]).Height >= 0))
                    {
                        sumHeight += ((SheetRow)mDRows[r]).Height;
                    }
                    else
                    {
                        sumHeight += Defaults.RowHeight;
                    }
                }
                scaleHeight = (double)bounds.Height / (double)sumHeight;
            }
            if (sps.KeepAspectRatio)
            {
                if (scaleHeight < scaleWidth) scaleWidth = scaleHeight; else scaleHeight = scaleWidth;
            }
            passedWidth = 0;
            addValue = (mRightToLeft == RightToLeft.Yes) ? -1 : 1;
            cS = (mRightToLeft == RightToLeft.Yes) ? endCell.X : startCell.X;
            cE = (mRightToLeft == RightToLeft.Yes) ? startCell.X : endCell.X;
            //rS = (mRightToLeft == RightToLeft.Yes) ? endCell.Y : startCell.Y;
            //rE = (mRightToLeft == RightToLeft.Yes) ? startCell.Y : endCell.Y;
            //addValue = 1;
            //cS = startCell.X;
            //cE = endCell.X;
            rS = startCell.Y;
            rE = endCell.Y;

            for (c = cS; c != cE + addValue; c += addValue)
            {
                xColumn = mDColumns.ContainsKey(c) ? (SheetColumn)mDColumns[c] : (new SheetColumn());
                if (xColumn.Width < 0) xColumn.Width = Defaults.ColumnWidth;
                passedHeight = 0;
                for (r = rS; r != rE + 1; r += 1)
                {
                    xRow = mDRows.ContainsKey(r) ? (SheetRow)mDRows[r] : (new SheetRow());
                    if (xRow.Height < 0) xRow.Height = Defaults.RowHeight;

                    key = BuildKey(r, c);
                    xCell = mDCells.ContainsKey(key) ? (SheetCell)mDCells[key] : (new SheetCell());
                    printCell = new SheetCell();
                    //Make cell as default cell
                    printCell.TextAlign = Defaults.TextAlign;
                    printCell.ForeColor = Defaults.ForeColor;
                    printCell.Font = Defaults.Font;
                    printCell.SetBorder(SheetBorderTypes.Left, Defaults.GetBorder(SheetBorderTypes.Left));
                    printCell.SetBorder(SheetBorderTypes.Right, Defaults.GetBorder(SheetBorderTypes.Right));
                    printCell.SetBorder(SheetBorderTypes.Top, Defaults.GetBorder(SheetBorderTypes.Top));
                    printCell.SetBorder(SheetBorderTypes.Bottom, Defaults.GetBorder(SheetBorderTypes.Bottom));
                    printCell.BackColor = Defaults.BackColor;
                    printCell.BorderColor = Defaults.BorderColor;
                    printCell.RightToLeft = Defaults.RightToLeft;
                    printCell.ShrinkToFit = Defaults.ShrinkToFit;
                    printCell.Warp = Defaults.Warp;
                    printCell.VerticalText = Defaults.VerticalText;
                    printCell.OwnerDraw = SheetOwnerDrawModes.None;
                    //Apply column properties
                    if (xColumn.TextAlign != 0) printCell.TextAlign = xColumn.TextAlign;
                    if (xColumn.Font != null) printCell.Font = xColumn.Font;
                    if (!xColumn.ForeColor.IsEmpty) printCell.ForeColor = xColumn.ForeColor;
                    if (xColumn.GetBorder(SheetBorderTypes.Left) >= 0) printCell.SetBorder(SheetBorderTypes.Left, xColumn.GetBorder(SheetBorderTypes.Left));
                    if (xColumn.GetBorder(SheetBorderTypes.Right) >= 0) printCell.SetBorder(SheetBorderTypes.Right, xColumn.GetBorder(SheetBorderTypes.Right));
                    if (xColumn.GetBorder(SheetBorderTypes.Top) >= 0) printCell.SetBorder(SheetBorderTypes.Top, xColumn.GetBorder(SheetBorderTypes.Top));
                    if (xColumn.GetBorder(SheetBorderTypes.Bottom) >= 0) printCell.SetBorder(SheetBorderTypes.Bottom, xColumn.GetBorder(SheetBorderTypes.Bottom));
                    if (!xColumn.BackColor.IsEmpty) printCell.BackColor = xColumn.BackColor;
                    if (xColumn.IsPropertyChanged("RightToLeft")) printCell.RightToLeft = xColumn.RightToLeft;
                    if (xColumn.IsPropertyChanged("ShrinkToFit")) printCell.ShrinkToFit = xColumn.ShrinkToFit;
                    if (xColumn.IsPropertyChanged("Warp")) printCell.Warp = xColumn.Warp;
                    if (xColumn.IsPropertyChanged("VerticalText")) printCell.VerticalText = xColumn.VerticalText;
                    //Apply row properties
                    if (xRow.TextAlign != 0) printCell.TextAlign = xRow.TextAlign;
                    if (xRow.Font != null) printCell.Font = xRow.Font;
                    if (!xRow.ForeColor.IsEmpty) printCell.ForeColor = xRow.ForeColor;
                    if (xRow.GetBorder(SheetBorderTypes.Left) >= 0) printCell.SetBorder(SheetBorderTypes.Left, xRow.GetBorder(SheetBorderTypes.Left));
                    if (xRow.GetBorder(SheetBorderTypes.Right) >= 0) printCell.SetBorder(SheetBorderTypes.Right, xRow.GetBorder(SheetBorderTypes.Right));
                    if (xRow.GetBorder(SheetBorderTypes.Top) >= 0) printCell.SetBorder(SheetBorderTypes.Top, xRow.GetBorder(SheetBorderTypes.Top));
                    if (xRow.GetBorder(SheetBorderTypes.Bottom) >= 0) printCell.SetBorder(SheetBorderTypes.Bottom, xRow.GetBorder(SheetBorderTypes.Bottom));
                    if (!xRow.BackColor.IsEmpty) printCell.BackColor = xRow.BackColor;
                    if (xRow.IsPropertyChanged("RightToLeft")) printCell.RightToLeft = xRow.RightToLeft;
                    if (xRow.IsPropertyChanged("ShrinkToFit")) printCell.ShrinkToFit = xRow.ShrinkToFit;
                    if (xRow.IsPropertyChanged("Warp")) printCell.Warp = xRow.Warp;
                    if (xRow.IsPropertyChanged("VerticalText")) printCell.VerticalText = xRow.VerticalText;
                    //Apply cell properties
                    if (xCell.TextAlign != 0) printCell.TextAlign = xCell.TextAlign;
                    if (xCell.Font != null) printCell.Font = xCell.Font;
                    if (!xCell.ForeColor.IsEmpty) printCell.ForeColor = xCell.ForeColor;
                    if (xCell.GetBorder(SheetBorderTypes.Left) >= 0) printCell.SetBorder(SheetBorderTypes.Left, xCell.GetBorder(SheetBorderTypes.Left));
                    if (xCell.GetBorder(SheetBorderTypes.Right) >= 0) printCell.SetBorder(SheetBorderTypes.Right, xCell.GetBorder(SheetBorderTypes.Right));
                    if (xCell.GetBorder(SheetBorderTypes.Top) >= 0) printCell.SetBorder(SheetBorderTypes.Top, xCell.GetBorder(SheetBorderTypes.Top));
                    if (xCell.GetBorder(SheetBorderTypes.Bottom) >= 0) printCell.SetBorder(SheetBorderTypes.Bottom, xCell.GetBorder(SheetBorderTypes.Bottom));
                    if (!xCell.BackColor.IsEmpty) printCell.BackColor = xCell.BackColor;
                    if (!xCell.BorderColor.IsEmpty) printCell.BorderColor = xCell.BorderColor;
                    if (xCell.Merge != SheetMergeTypes.None) printCell.Merge = xCell.Merge;
                    if (xCell.Text != null) printCell.Text = xCell.Text;
                    if (xCell.OwnerDraw != SheetOwnerDrawModes.None) printCell.OwnerDraw = xCell.OwnerDraw;
                    if (xCell.IsPropertyChanged("RightToLeft")) printCell.RightToLeft = xCell.RightToLeft;
                    if (xCell.IsPropertyChanged("ShrinkToFit")) printCell.ShrinkToFit = xCell.ShrinkToFit;
                    if (xCell.IsPropertyChanged("Warp")) printCell.Warp = xCell.Warp;
                    if (xCell.IsPropertyChanged("VerticalText")) printCell.VerticalText = xCell.VerticalText;

                    //Use printCell data to draw cell
                    int sX, sY, xH, xW;
                    StringFormat xSF = new StringFormat();
                    int i, xValue, mergeCellCountH, mergeCellCountV;
                    //Draw border and background
                    xH = (int)(xRow.Height * scaleHeight);
                    xW = (int)(xColumn.Width * scaleWidth);
                    sX = bounds.X + passedWidth; sY = bounds.Y + passedHeight;
                    if (printCell.Merge != SheetMergeTypes.None)
                    {
                        if (((printCell.Merge & SheetMergeTypes.Top) != SheetMergeTypes.Top) && ((printCell.Merge & SheetMergeTypes.Left) != SheetMergeTypes.Left))
                        {
                            xCell = printCell;
                            cellWidth = xW;
                            i = 1;
                            mergeCellCountH = 0;
                            while ((xCell.Merge & SheetMergeTypes.Right) == SheetMergeTypes.Right)
                            {
                                xW += mDColumns.ContainsKey(c + i) ? (int)(((SheetColumn)mDColumns[c + i]).Width * scaleWidth) : (int)(Defaults.ColumnWidth * scaleWidth);
                                key = BuildKey(r, c + i);
                                xCell = mDCells.ContainsKey(key) ? (SheetCell)mDCells[key] : (new SheetCell());
                                i += 1;
                                mergeCellCountH++;
                            }
                            xCell = printCell;
                            i = 1;
                            mergeCellCountV = 0;
                            while ((xCell.Merge & SheetMergeTypes.Bottom) == SheetMergeTypes.Bottom)
                            {
                                xH += mDRows.ContainsKey(r + i) ? (int)(((SheetRow)mDRows[r + i]).Height * scaleHeight) : (int)(Defaults.RowHeight * scaleHeight);
                                key = BuildKey(r + i, c);
                                xCell = mDCells.ContainsKey(key) ? (SheetCell)mDCells[key] : (new SheetCell());
                                i += 1;
                                mergeCellCountV++;
                            }
                            if (mRightToLeft == RightToLeft.Yes) sX -= (xW - cellWidth);
                            key = BuildKey(r + mergeCellCountV, c + mergeCellCountH);
                            xCell = (mDCells.ContainsKey(key)) ? (SheetCell)mDCells[key] : (new SheetCell());
                            printCell.SetBorder(SheetBorderTypes.Bottom, xCell.GetBorder(SheetBorderTypes.Bottom));
                            printCell.SetBorder(SheetBorderTypes.Right, xCell.GetBorder(SheetBorderTypes.Right));
                        }
                        else
                        {
                            goto NextCell;
                        }
                    }
                    if (printCell.OwnerDraw == SheetOwnerDrawModes.Full)
                    {
                        if (SheetCellOwnerDraw != null)
                            SheetCellOwnerDraw(g, new Rectangle(sX, sY, xW, xH), printCell, r, c);
                    }
                    else
                    {
                        if (!printCell.BackColor.IsEmpty) g.FillRectangle(new SolidBrush(printCell.BackColor), sX, sY, xW, xH);
                        xValue = printCell.GetBorder(SheetBorderTypes.Top);
                        if (xValue > 0)
                            g.DrawLine(new Pen(printCell.BorderColor, xValue), sX - (Single)(xValue / 2), sY, sX + xW + (Single)(xValue / 2), sY);
                        sY += (xValue / 2); xH -= (xValue / 2);
                        xValue = printCell.GetBorder(SheetBorderTypes.Bottom);
                        if (xValue > 0)
                            g.DrawLine(new Pen(printCell.BorderColor, xValue), sX - (Single)(xValue / 2), sY + xH, sX + xW + (Single)(xValue / 2), sY + xH);
                        xH -= (xValue / 2);

                        //IMPORTANT! For Right To Left Support
                        sbt1 = (mRightToLeft == RightToLeft.Yes) ? SheetBorderTypes.Right : SheetBorderTypes.Left;
                        xValue = printCell.GetBorder(sbt1);
                        if (xValue > 0)
                            g.DrawLine(new Pen(printCell.BorderColor, xValue), sX, sY - (Single)(xValue / 2), sX, sY + xH + (Single)(xValue / 2));
                        sX += (xValue / 2); xW -= (xValue / 2);

                        sbt1 = (mRightToLeft == RightToLeft.Yes) ? SheetBorderTypes.Left : SheetBorderTypes.Right;
                        xValue = printCell.GetBorder(sbt1);
                        if (xValue > 0)
                            g.DrawLine(new Pen(printCell.BorderColor, xValue), sX + xW, sY - (Single)(xValue / 2), sX + xW, sY + xH + (Single)(xValue / 2));
                        xW -= (xValue / 2);

                        //IMPORTANT! For Right To Left Support

                        //Draw content
                        if (printCell.OwnerDraw == SheetOwnerDrawModes.Content)
                        {
                            SheetCellOwnerDraw(g, new Rectangle(sX, sY, xW, xH), printCell, r, c);
                        }
                        else
                        {
                            if (printCell.Text is IPrintOn)
                            {
                                ((IPrintOn)printCell.Text).PrintOn(g, new Rectangle(sX, sY, xW, xH));
                            }
                            else if (printCell.Text is Image)
                            {
                                g.DrawImage((Image)printCell.Text, new Rectangle(sX, sY, xW, xH));
                            }
                            else if (printCell.Text is Bitmap)
                            {
                                g.DrawImage(Image.FromHbitmap(((Bitmap)printCell.Text).GetHbitmap()), new Rectangle(sX, sY, xW, xH));
                            }
                            else if (printCell.Text != null)
                            {
                                str1 = printCell.Text.ToString();
                                if (str1 != "")
                                {
                                    switch (printCell.TextAlign)
                                    {
                                        case ContentAlignment.BottomCenter:
                                            xSF.Alignment = StringAlignment.Center;
                                            xSF.LineAlignment = StringAlignment.Far;
                                            break;
                                        case ContentAlignment.BottomLeft:
                                            xSF.Alignment = StringAlignment.Near;
                                            xSF.LineAlignment = StringAlignment.Far;
                                            break;
                                        case ContentAlignment.BottomRight:
                                            xSF.Alignment = StringAlignment.Far;
                                            xSF.LineAlignment = StringAlignment.Far;
                                            break;
                                        case ContentAlignment.MiddleCenter:
                                            xSF.Alignment = StringAlignment.Center;
                                            xSF.LineAlignment = StringAlignment.Center;
                                            break;
                                        case ContentAlignment.MiddleLeft:
                                            xSF.Alignment = StringAlignment.Near;
                                            xSF.LineAlignment = StringAlignment.Center;
                                            break;
                                        case ContentAlignment.MiddleRight:
                                            xSF.Alignment = StringAlignment.Far;
                                            xSF.LineAlignment = StringAlignment.Center;
                                            break;
                                        case ContentAlignment.TopCenter:
                                            xSF.Alignment = StringAlignment.Center;
                                            xSF.LineAlignment = StringAlignment.Near;
                                            break;
                                        case ContentAlignment.TopLeft:
                                            xSF.Alignment = StringAlignment.Near;
                                            xSF.LineAlignment = StringAlignment.Near;
                                            break;
                                        case ContentAlignment.TopRight:
                                            xSF.Alignment = StringAlignment.Far;
                                            xSF.LineAlignment = StringAlignment.Near;
                                            break;
                                    }
                                    if (printCell.RightToLeft) xSF.FormatFlags |= StringFormatFlags.DirectionRightToLeft;
                                    if (printCell.VerticalText) xSF.FormatFlags |= StringFormatFlags.DirectionVertical;
                                    if (!printCell.Warp) xSF.FormatFlags |= StringFormatFlags.NoWrap;
                                    if (DigitType == DigitTypes.Persian) xSF.SetDigitSubstitution(new System.Globalization.CultureInfo("fa-IR").LCID, StringDigitSubstitute.National);
                                    if (printCell.ShrinkToFit)
                                    {
                                        SizeF textSize = g.MeasureString(str1, printCell.Font, new SizeF(2 * xW, 2 * xH), xSF);
                                        //textW = (printCell.VerticalText) ? textSize.Height : textSize.Width;
                                        //textH = (printCell.VerticalText) ? textSize.Width : textSize.Height;
                                        textW = textSize.Width;
                                        textH = textSize.Height;
                                        while ((textW > xW) || (textH > xH))
                                        {
                                            if (printCell.Font.Size <= 1) break;
                                            printCell.Font = new Font(printCell.Font.FontFamily, printCell.Font.Size - 1, printCell.Font.Style);
                                            textSize = g.MeasureString(str1, printCell.Font, new SizeF(2 * xW, 2 * xH), xSF);
                                            //textW = (printCell.VerticalText) ? textSize.Height : textSize.Width;
                                            //textH = (printCell.VerticalText) ? textSize.Width : textSize.Height;
                                            textW = textSize.Width;
                                            textH = textSize.Height;
                                        }
                                    }
                                    g.DrawString(str1, printCell.Font, new SolidBrush(printCell.ForeColor), new RectangleF(sX, sY, xW, xH), xSF);
                                }
                            }
                        }
                    }
                NextCell:
                    passedHeight += (int)(xRow.Height * scaleHeight);
                }
                passedWidth += (int)(xColumn.Width * scaleWidth);
            }
        }

        private SheetPrintSettings mPrintSettings = new SheetPrintSettings();
        public SheetPrintSettings PrintSettings
        {
            get { return mPrintSettings; }
        }

        #region ISerializable Members

        public Sheet(SerializationInfo si, StreamingContext context)
        {
            Defaults = (SheetDefaults)si.GetValue("defaults", typeof(SheetDefaults));
            mDRows = (Hashtable)si.GetValue("rows", typeof(Hashtable));
            mDColumns = (Hashtable)si.GetValue("cols", typeof(Hashtable));
            mDCells = (Hashtable)si.GetValue("cells", typeof(Hashtable));
            mPrintAreaStartCell = (Point)si.GetValue("printStartCell", typeof(Point));
            mPrintAreaEndCell = (Point)si.GetValue("printEndCell", typeof(Point));
            DigitType = (DigitTypes)si.GetValue("digitType", typeof(DigitTypes));
            try
            {
                mRightToLeft = (System.Windows.Forms.RightToLeft)si.GetValue("rightToLeft", typeof(RightToLeft));
            }
            catch (Exception)
            {
                mRightToLeft = RightToLeft.No;
            }
        }

        public void GetObjectData(SerializationInfo info, StreamingContext context)
        {
            info.AddValue("defaults", Defaults);
            info.AddValue("rows", mDRows);
            info.AddValue("cols", mDColumns);
            info.AddValue("cells", mDCells);
            info.AddValue("printStartCell", mPrintAreaStartCell);
            info.AddValue("printEndCell", mPrintAreaEndCell);
            info.AddValue("digitType", DigitType);
            info.AddValue("rightToLeft", mRightToLeft);
        }

        #endregion

        #region IPrintOn Members

        public void PrintOn(Graphics g, Rectangle bounds)
        {
            SheetPrintSettings mySPS = mPrintSettings.Clone() as SheetPrintSettings;
            mySPS.AutoPage = false;
            mySPS.Bounds = bounds;
            mySPS.Graphics = g;
            InternalPrint(mySPS, mPrintAreaStartCell, mPrintAreaEndCell);
            //Print();
        }

        #endregion
    }
}
